home *** CD-ROM | disk | FTP | other *** search
- Path: xanth!nic.MR.NET!csd4.milw.wisc.edu!leah!itsgw!steinmetz!uunet!allbery
- From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
- Newsgroups: comp.sources.misc
- Subject: v06i028: New version of secure mkdir
- Message-ID: <47761@uunet.UU.NET>
- Date: 29 Jan 89 21:23:48 GMT
- Sender: allbery@uunet.UU.NET
- Reply-To: doug@hal.CWRU.Edu@i.UUCP (Doug Davis at letni.UUCP)
- Lines: 520
- Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
-
- Posting-number: Volume 6, Issue 28
- Submitted-by: doug@hal.CWRU.Edu@i.UUCP (Doug Davis at letni.UUCP)
- Archive-name: smkdir3
-
- [Still?! ++bsa]
-
- here is the lastest, and hopefully the last version of my secure mkdir
- program, if its possable replace all copys of the previous one with
- this one. Of course as soon as all the bugs get are found *AND* reported
- there might be another one. ;-)
-
- doug
-
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: Makefile mkdir.c
- # Wrapped by doug@letni.UUCP on Thu Jan 26 20:05:38 1989
- # This shar file will self destruct in 4 seconds, good luck %s.
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(536 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- X# What kind of strrchr do we have?
- X# 'strrchr' for most newer unix's, 'rindex' for earlier editions
- X# STTRCHR = -DSTRRCHR=rindex
- STRRCHR = -DSTRRCHR=strrchr
- X
- X# Do you have a rand() function call?
- X# If you don't have one, comment out the line below
- RAND = -DRAND
- X
- X# for debugging, Does not unlink the secure tree.
- X# DEBUG = -DDEBUG
- X
- DEFINES = $(STRRCHR) $(DEBUG) $(RAND)
- SHELL = /bin/sh
- CC = /bin/cc
- CFLAGS = $(DEFINES) -O
- LDFLAGS = -n
- X
- mkdir: mkdir.o
- X $(CC) mkdir.o $(LDFLAGS) -o mkdir
- X
- mkdir.o: mkdir.c
- X $(CC) $(CFLAGS) -c mkdir.c
- END_OF_FILE
- if test 536 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'mkdir.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mkdir.c'\"
- else
- echo shar: Extracting \"'mkdir.c'\" \(11864 characters\)
- sed "s/^X//" >'mkdir.c' <<'END_OF_FILE'
- char version[]="mkdir (1.007)";
- X
- X/*
- X * Secure mkdir program, solves that nasty race problem...
- X *
- X * 13 December 1988 Doug Davis doug@letni.lawnet.com
- X * and John Elliott IV iv@trsvax.tandy.com
- X *
- X * Modification History:
- X * M007 9 Jan 89 Gordon Burditt gordon@trsvax.tandy.com
- X * - Fixed the signal handler to properly handle
- X * previously ignored signals.
- X * - Fixed canIwrite() to follow SVID conventions.
- X * M006 6 Jan 89 Doug Davis doug@letni.lawnet.com
- X * - Fixed M005 so it would not leave the secure
- X * tree lying around after recipt of multple
- X * signals.
- X * M005 5 Jan 89 John Haugh II jfh@rpp386.dallas.tx.us
- X * - Added code to abort without createing all of the
- X * directoies that were specified.
- X * M004 22 Dec 88 Doug Davis doug@letni.lawnet.com
- X * - Fixed the error handler for the second makedir() call
- X * so it will unlink the securedir if it fails.
- X * - Fixed the error handler for chown() so that
- X * it removes the working "secure" directories.
- X * if it ever fails
- X * - cleaned up some odd tabs in the comments, and ran
- X * them thru a spell checker.
- X * M003 22 Dec 88 John Elliott IV iv@trsvax.tandy.com
- X * - Fixed the spelling of my name
- X * - General readability cleanup
- X * - Fixed backwards #ifdef RAND for srand() call
- X * - Saved some storage by declaring error msgs char msg[]
- X * - Fixed call order in "cleanup" code so that if the
- X * unlink() of the securename fails, we will still try
- X * to remove our scratch directory
- X * - Some calls to error() had an errno of 0, which meant
- X * that mkdir would incorrectly exit 0. Calls to error()
- X * now include an exit code, which error() now exits with.
- X * - Fixed case where link-into-place failed and left all
- X * our flotsam and jetsam around. Now rmdir() everything
- X * before we call error().
- X * - Changed the two error message symbols which began with
- X * "Couldnt_" to begin with "Cant_" so the symbols will
- X * be significant in 7 characters, for those so unfortunate
- X * not to have flexnames.
- X * M002 21 Dec 88 Doug Davis doug@letni.lawnet.com
- X * - Cleaned up code, added function declarations
- X * - fixed bug in second stat's unlink code
- X * M001 19 Dec 88 John Haugh II jfn@rpp386
- X * - Fixes oops in original code, much more secure now.
- X *
- X * Theory of operation:
- X * This mkdir first makes a directory to play in, which is
- X * owned by root and is mode 000. It is made in the same
- X * directory in which the user is requesting his directory.
- X * In this "secure" directory, to which the user allegedly
- X * has no access, the mknod(), chown(), and links for `.'
- X * and `..' are performed. The new directory is then linked
- X * into place. Finally, the "secure" directory is removed.
- X *
- X * This code copyright 1988 by Doug Davis (doug@letni.lawnet.com)
- X * You are free to modify, hack, fold, spindle, duplicate, pass-along
- X * give-away, publish, transmit, or mutlate this code in any maner,
- X * provided that you give credit where credit is due and don't pretend
- X * that you wrote it.
- X *
- X * If you do my lawyers (and I have a lot of lawyers) will teach you a lesson
- X * or two in copyright law that you will never ever forget.
- X *
- X */
- X
- X#define MAXPATHLEN 128 /* maximum reasonanble path length */
- X
- X#include <sys/types.h>
- X#include <signal.h>
- X#include <sys/stat.h>
- X#ifdef DEBUG
- X# include <stdio.h>
- X#else /*DEBUG*/
- X# define NULL 0
- X#endif /*DEBUG*/
- X
- X#define MKNODE 1
- X#define LINK 2
- X
- char Malloc_Failed[] = "malloc() failed.";
- char Doesnt_Exist[] = " does not exist.";
- char Cannot_Access[] = "cannot access ";
- char Already_Exist[] = " already exists.";
- char Secure_Failed[] = "makedir secure parent failed ";
- char Cant_Link[] = "Couldn't link to ";
- char Mkdir_Failed[] = "makedir failed ";
- char Chown_Failed[] = "chown() failed ";
- char Cant_Unlink[] = "Couldn't unlink ";
- X
- extern char *STRRCHR();
- extern char *malloc();
- X
- extern int errno;
- extern int getpid();
- extern int stat();
- extern int chown();
- extern int unlink();
- extern int strlen();
- X
- extern unsigned short getuid();
- extern unsigned short getgid();
- extern long time();
- X
- X
- char *Progname;
- int signaled = 0;
- X
- X#ifdef RAND
- X extern int rand();
- X#else /*RAND*/
- X extern int getppid();
- X#endif /*RAND*/
- X
- X/* This function is part of the signal handler, it is used to prevent
- X * mkdir from creating all of the directories on the command line,
- X * upon recipt of a signal mkdir will finish the directory that it is
- X * currently doing then exit
- X */
- int
- catch()
- X{
- X signaled = 1;
- X set_sigs(SIG_IGN);
- X}
- X
- main(argc, argv)
- int argc;
- char *argv[];
- X{
- X Progname = argv[0];
- X errno = 0;
- X
- X if (argc < 2) {
- X print("Usage: ");
- X print(Progname);
- X print(" directory_name [ ... directory_name ]\n");
- X exit(0);
- X }
- X
- X /* Catch those nasty signals that could cause us
- X * to mess up the filesystem */
- X set_sigs(catch);
- X
- X while (--argc && ! signaled)
- X md(*++argv); /* make each directory */
- X
- X exit(0);
- X}
- X
- X
- int
- md(s)
- char *s;
- X{
- X register char *basename,
- X *parent,
- X *fullname;
- X
- X unsigned short myuserid,
- X mygroupid;
- X
- X char securename[MAXPATHLEN],
- X securedir[MAXPATHLEN],
- X dotsecurename[MAXPATHLEN];
- X
- X int rval1,
- X rval2;
- X
- X long snum;
- X
- X struct stat sanity;
- X
- X /* find out who I really am */
- X myuserid = getuid();
- X mygroupid = getgid();
- X
- X /* set up the pseudo-RANDom number generation system */
- X#ifdef RAND
- X srand(getpid());
- X#endif /*RAND*/
- X
- X /* see if we are explicit or indirect */
- X basename = STRRCHR(s, '/');
- X if (basename == (char *)NULL) {
- X fullname = malloc(strlen(s)+1);
- X if (fullname == (char *)NULL)
- X error(Malloc_Failed, (char *)NULL, errno, 1);
- X parent = malloc(2);
- X if (parent == (char *)NULL)
- X error(Malloc_Failed, (char *)NULL, errno, 1);
- X parent[0] = '.';
- X parent[1] = '\0';
- X strcpy(fullname, s);
- X basename = s;
- X } else {
- X fullname = malloc(strlen(s)+1);
- X if (fullname == (char *)NULL)
- X error(Malloc_Failed, (char *)NULL, errno, 1);
- X strcpy(fullname, s);
- X *basename = '\0';
- X basename++;
- X parent = malloc(strlen(s) + 3);
- X if (parent == (char *)NULL)
- X error(Malloc_Failed, (char *)NULL, errno, 1);
- X strcpy(parent, s);
- X strcat(parent, "/.");
- X }
- X
- X /* Generate the secure names ... */
- X do {
- X /* round and round we go; where we stop depends on
- X * the non-existance of securedir */
- X snum = time((long *) 0L);
- X#ifdef RAND
- X sprintf(securedir, "%s/%ld", parent,
- X (rand() % 2) ? snum + (long)rand() : snum - (long)rand());
- X sprintf(securename, "%s/%ld", securedir,
- X (rand() % 2) ? snum - (long)rand() : snum + (long)rand());
- X sprintf(dotsecurename, "%s/./.", securename);
- X#else /*RAND*/
- X sprintf(securedir, "%s/%ld", parent, snum - (long)getppid());
- X sprintf(securename, "%s/%ld", securedir, snum + (long)getppid());
- X sprintf(dotsecurename, "%s/./.", securename);
- X snum += (long)getpid();
- X#endif /*RAND*/
- X } while (stat(securedir, &sanity) == 0);
- X
- X#ifdef DEBUG
- X /* spill the beans .. */
- X printf("parent == %s\n", parent);
- X printf("basename == %s\n", basename);
- X printf("fullname == %s\n", fullname);
- X printf("securedir == %s\n", securedir);
- X printf("securename == %s\n", securename);
- X printf("dotsecurename == %s\n", dotsecurename);
- X fflush(stdout);
- X#endif /*DEBUG*/
- X
- X /* let's see if our parent directory is around... */
- X if ((stat(parent, &sanity)) != 0)
- X error(parent, Doesnt_Exist, 0, 2);
- X
- X /* find out if we can write here */
- X if (canIwrite(&sanity, myuserid, mygroupid) != 0)
- X error(Cannot_Access, parent, 0, 2);
- X
- X /* find out if we are going to stomp on something.. */
- X if ((stat(fullname, &sanity)) == 0)
- X error(fullname, Already_Exist, 0, 2);
- X
- X /* make secure parent directory (note the mode of 0) */
- X if (makedir(parent, securedir, 0) > 0)
- X error(Secure_Failed, securedir, errno, 2);
- X
- X /* now make our directory underneath it */
- X if (makedir(parent, securename, 0777) > 0) {
- X rmdir(securedir);
- X error(Mkdir_Failed, securedir, errno, 3);
- X }
- X
- X /* do that eerie little chown() that's the "root" of all our problems */
- X if (chown(dotsecurename, myuserid, mygroupid) != 0) {
- X rmdir(securename);
- X rmdir(securedir);
- X error(Chown_Failed, dotsecurename, errno, 3);
- X }
- X
- X /* do a quick sanity check, just to annoy someone trying
- X * to trick mkdir into chowning something it shouldn't.. */
- X if ((stat(fullname, &sanity)) == 0) {
- X /* What happened? This wasn't here a couple of calls ago... */
- X rmdir(securename);
- X rmdir(securedir);
- X error(fullname, Already_Exist, 0, 4);
- X }
- X
- X /* okay, put it where it belongs */
- X if ((link(securename, fullname)) < 0) {
- X rmdir(securename);
- X rmdir(securedir);
- X error(Cant_Link, fullname, errno, 4);
- X }
- X
- X /* remove all our rubbish, and tidy everything up.. */
- X if (parent != (char *)NULL)
- X free(parent);
- X if (fullname != (char *)NULL)
- X free(fullname);
- X rval1 = unlink(securename);
- X /* Even if the unlink() fails, we really should at least
- X * attempt to remove our scratch directory... */
- X rval2 = rmdir(securedir);
- X if (rval1 < 0)
- X error(Cant_Unlink, securename, errno, 5);
- X if (rval2 != 0)
- X error(Cant_Unlink, securedir, errno, 5);
- X
- X return (0);
- X}
- X
- int
- makedir(parent, dir, mode)
- char *parent, *dir;
- int mode;
- X{
- X char dotdot[MAXPATHLEN];
- X
- X#ifdef DEBUG
- X printf("mkdir(%s, %s)\n", parent, dir);
- X fflush(stdout);
- X#endif /*DEBUG*/
- X
- X /* put the node together */
- X if ((mknod(dir, S_IFDIR | mode, 0)) < 0)
- X return (MKNODE);
- X
- X /* make dot */
- X strcpy(dotdot, dir);
- X strcat(dotdot, "/.");
- X if ((link(dir, dotdot)) < 0)
- X return (LINK);
- X
- X /* make dotdot */
- X strcat(dotdot, ".");
- X if ((link(parent, dotdot)) < 0)
- X return (LINK);
- X
- X return (0);
- X}
- X
- int
- rmdir(dir)
- char *dir;
- X{
- X char dots[MAXPATHLEN];
- X
- X#ifdef DEBUG
- X printf("rmdir(%s)\n", dir);
- X fflush(stdout);
- X#endif /*DEBUG*/
- X
- X strcpy(dots, dir);
- X strcat(dots, "/.");
- X
- X /* unlink(".") */
- X if (unlink(dots) < 0)
- X return (LINK);
- X
- X /* unlink("..") */
- X strcat(dots, ".");
- X if (unlink(dots) < 0)
- X return (LINK);
- X
- X /* unlink the directory itself */
- X if (unlink(dir) < 0)
- X return (LINK);
- X
- X return (0);
- X}
- X
- int
- print(s)
- char *s;
- X{
- X return (write(2, s, strlen(s)));
- X}
- X
- error(s1, s2, err, ecode)
- char *s1,
- X *s2;
- int err,
- X ecode;
- X{
- X print(Progname);
- X print(": ");
- X print(s1);
- X errno = err;
- X if (s2 != (char *)NULL)
- X print(s2);
- X if (err != 0)
- X perror(" ");
- X else
- X print("\n");
- X exit(ecode);
- X}
- X
- int
- set_sigs(func)
- int *(*func)();
- X{
- X register int i;
- X
- X for (i=1; i<=NSIG ; i++)
- X if (signal(i, SIG_IGN) != SIG_IGN)
- X signal(i, func); /* point it at the catch() routine. */
- X
- X return (0);
- X}
- X
- int
- canIwrite(stbuff, uid, gid)
- register struct stat *stbuff;
- register unsigned short uid,
- X gid;
- X{
- X /* we let root get away with anything... */
- X if (uid == 0)
- X return (0);
- X
- X /* can I write in it as an OWNER ? */
- X if (uid == stbuff->st_uid)
- X if (stbuff->st_mode & 0200)
- X return (0);
- X else
- X /* okay, so I can't write here.. */
- X return (-1);
- X
- X /* okay, so how about as a GROUP ? */
- X if (gid == stbuff->st_gid)
- X if (stbuff->st_mode & 0020)
- X return (0);
- X else
- X /* okay, so I can't write here.. */
- X return (-1);
- X
- X /* alright, how about an OTHER ? */
- X if (stbuff->st_mode & 0002)
- X return (0);
- X
- X /* okay, so I can't write here.. */
- X return (-1);
- X}
- X
- X#ifdef DEBUG
- unlink(s)
- char *s;
- X{
- X printf("Unlink(%s)\n", s);
- X fflush(stdout);
- X}
- X#endif /*DEBUG*/
- END_OF_FILE
- if test 11864 -ne `wc -c <'mkdir.c'`; then
- echo shar: \"'mkdir.c'\" unpacked with wrong size!
- fi
- # end of 'mkdir.c'
- fi
- echo shar: End of shell archive.
- exit 0
-